#include "HashExtensible.h"
#include <string>

using namespace std;
using namespace Comun;

namespace CapaIndices{
		HashExtensible::HashExtensible(MetadatosIndice* metadata){
			this->Metadata = metadata;
			this->directoryDepth = 0;
			for (IteradorDeClaveValor i = Metadata->Atributos.begin(); i!= Metadata->Atributos.end();i++)
			{
				ClaveValor cv = *i;
				if (cv.Clave == "tamanoBucket")
					tamanioBucketBytes = (long) atoi(&cv.Valor[0]);
				if (cv.Clave == "organizacionRegistros"){
					organizacionReg = (char*)malloc(sizeof(char) * strlen(cv.Valor.c_str()));
					organizacionReg = strcpy(organizacionReg, cv.Valor.c_str());
					//cambio el valor a RLV si es RVF
					if (strcmp(organizacionReg, "RVF") == 0)
						organizacionReg = strcpy(organizacionReg, "RLV");

				}

			}
			tipoIndice = Metadata->Tipo;
		}

		bool HashExtensible::Crear(){
			/*
			 * El Archivo de Hash es la tabla de Hash con los distintos valores
			 * posibles que puede tomar el resultado de aplicar MOD a la clave.
			 * Se lo trata como un archivo con un grupo inicialmente. El grupo contiene
			 * un atributo de tipo Cadena con las distintas posiciones a los buckets, las cuales
			 * se indexan segun el resultado del MOD.
			 * Cuando es necesario duplicar la cantidad de direcciones, se duplica la cadena.
			 * Si ya no entra en el registro, se crea otro grupo.
			 */
			MetadatosArchivo metArchHash = MetadatosArchivo(Metadata->NombreArchivo + "Hash");
			MetadatosGrupo*  metGrupHash = new MetadatosGrupo();
			metGrupHash->MetadatosAtributos.insert(metGrupHash->MetadatosAtributos.begin(), MetadatosAtributo("OffsetBucket", Cadena)); //en realidad es un id de grupo en el archivo de Bucketsº
			metGrupHash->MetadatosRegistro = new MetadatosRegistro();
			metArchHash.MetadatosGrupo = metGrupHash;

			this->ArchivoHash = new ArchivoRF(metArchHash);

			/*
			 * El Archivo de Indice. Contiene grupos con registros de long. variable.
			 * Cada grupo representa un bucket.
			 */
			MetadatosArchivo metArchIndice = MetadatosArchivo(Metadata->NombreArchivo + "Indice");
			MetadatosGrupo* metGrupo = new MetadatosGrupo();
			MetadatosRegistro* metRegistro = new MetadatosRegistro(); //SUPONEMOS QUE EL CONSTRUCTOR CREA TAMBIEN LAS LISTAS CONTENIDAS

			// Agrega los atributos que tiene el grupo:
			MetadatosAtributo* cantidad = new MetadatosAtributo("Cantidad",Entero);
			metGrupo->MetadatosAtributos.insert(metGrupo->MetadatosAtributos.begin(),*cantidad);
			MetadatosAtributo* prof = new MetadatosAtributo("Profundidad",Entero);
			metGrupo->MetadatosAtributos.insert(metGrupo->MetadatosAtributos.begin(),*prof);
			// Seteo los atributos que van a tener los registros, incluidos en cada grupo
			// Agrega los campos de la clave
			metRegistro->MetadatosAtributos = Metadata->Claves;
			metRegistro->MetadatosAtributos.insert(metRegistro->MetadatosAtributos.end(), MetadatosAtributo("offsetGrupo", EnteroLargo));
			metGrupo->MetadatosRegistro = metRegistro;
			metArchIndice.MetadatosGrupo = metGrupo;
			this->ArchivoIndice = new ArchivoRLV(metArchIndice);//ArchivoRLV
			//this->ArchivoIndice->Metadatos = &metArchIndice;
			//Archivo de Datos.
			MetadatosArchivo metArchDatos = MetadatosArchivo(Metadata->NombreArchivo + "Datos");
			MetadatosGrupo*  metGrupDatos = new MetadatosGrupo();
			MetadatosRegistro* metRegDatos = new MetadatosRegistro();
			switch (Metadata->Tipo){
				case Griego:
					metRegDatos->MetadatosAtributos = Metadata->Claves;
					metRegDatos->MetadatosAtributos.insert(metRegDatos->MetadatosAtributos.end(), Metadata->CamposRegsitro.begin(), Metadata->CamposRegsitro.end());
					//Tengo que crear el archivo de Datos al cual apunta el Atributo offsetGrupo, de cada registro
					if (strcmp(organizacionReg, "RLV") == 0){
						//El Archivo de Datos tiene en cada grupo varios registros con los datos en si. Por eso agrego un atributo cantidad
						metGrupDatos->MetadatosAtributos.insert(metGrupDatos->MetadatosAtributos.begin(), MetadatosAtributo("cantidadRegDatos", Entero));
					}
					metGrupDatos->MetadatosRegistro = metRegDatos;
					metArchDatos.MetadatosGrupo = metGrupDatos;
					this->ArchivoDatos = new ArchivoRLV(metArchDatos);//ArchivoRLV
					//this->ArchivoDatos->Metadatos = &metArchDatos;

					break;
				case Romano:
					// Crear Archivo de Listas de referencias
					metRegDatos->MetadatosAtributos = Metadata->Claves;
					metRegDatos->MetadatosAtributos.insert(metRegDatos->MetadatosAtributos.end(), Metadata->ClavePrimaria.begin(), Metadata->ClavePrimaria.end());
					//El Archivo de Listas tiene en cada grupo varios registros con los datos de las claves primarias que se corresponden. Por eso agrego un atributo cantidad
					metGrupDatos->MetadatosAtributos.insert(metGrupDatos->MetadatosAtributos.begin(), MetadatosAtributo("cantidadRegReferencia", Entero));
					metGrupDatos->MetadatosRegistro = metRegDatos;
					metArchDatos.MetadatosGrupo = metGrupDatos;
					this->ArchivoDatos = new ArchivoRLV(metArchDatos);
					//this->ArchivoDatos->Metadatos = &metArchDatos;
					break;
			}


			//levanto en memoria la tabla de hash
			list<long>* listaAuxHash = new list<long>(); //es una lista auxiliar. Despues lo paso a tablaHsh
			int cantidadCampos = 0;
 			int idGrupo = this->ArchivoHash->PrimerGrupo();
			try{
			while (idGrupo != -1){
				//TODO: OJO, aca no tengo las primitivas implementadas.
				/*
				 * supongo que devuelve -1 cuando no hay mas grupos. Revisar tambien la llamada al grupo siguiente
				 * La idea es ir recorriendolos, tomando el string que es el unico regstro
				 * del grupo y parsearlo en los distintos offset's
				 */
				Grupo gActual = this->ArchivoHash->LeerGrupo(idGrupo);
				Atributo atribActual = *(gActual.Atributos.begin());
				//lo paso a un char*
				char* atribActualAsChar = new char[atribActual.Valor.length() + 1];
				atribActualAsChar = strcpy(atribActualAsChar, atribActual.Valor.c_str());
				int longCadena = atribActual.Valor.length();
				for (int indicadorCadena = 0; indicadorCadena < longCadena; indicadorCadena += ARCH_HASH_LONG_CADENA){
					char* valorAgregarAsChar = new char[ARCH_HASH_LONG_CADENA + 1];
					valorAgregarAsChar = (char*)memcpy(valorAgregarAsChar, &atribActualAsChar[indicadorCadena], ARCH_HASH_LONG_CADENA);
					valorAgregarAsChar[ARCH_HASH_LONG_CADENA] = '\n';
					long valorAgregar = (long)atoi(valorAgregarAsChar);
					listaAuxHash->insert(listaAuxHash->end(), valorAgregar);
					cantidadCampos++;
				}

				idGrupo = this->ArchivoHash->SiguienteGrupo(idGrupo);
			}
			}catch(...){}
			//lo paso a tablaHash
			tablaHash = new long[cantidadCampos]();
			for(int k = 0; k < cantidadCampos; k++)
				tablaHash[k] = -1; //inicializo con valores -1
			int iTable = 0;
			for (list<long>::iterator itTablaH = listaAuxHash->begin(); itTablaH != listaAuxHash->end(); itTablaH++){
				long valor = *itTablaH;
				tablaHash[iTable] = valor;
				iTable++;
			}
			float sq = sqrt((float)cantidadCampos);

			//segun la cantidad de registros que tiene la tabla inicializo dirDepth
			directoryDepth = (long)floor(sq);
			return true;
		}

		string HashExtensible::IntToString(int i){
                string s;
                stringstream ss;
                ss << i;
                ss >> s;

                return s;
        }

        string HashExtensible::IntToString(long i){
                string s;
                stringstream ss;
                ss << i;
                ss >> s;

                return s;
        }

		void HashExtensible::InsertarClave(Clave* clave){
			/*
			 * La operatoria es la siguiente:
			 * -Se calcula el hashing para el nuevo registro que va a ser insertado
			 * -Se obtiene el id de grupo del Archivo de Indice, es decir del bucket.
			 * --Si no existe el grupo, se crea
			 * -Ingreso los valores correspondientes en el bucket
			 * --Si el grupo esta vacio (sin registros),
			 * se crea tambien un grupo en el archivo de datos, y se ingresan los datos
			 * --Si no esta vacio, se ingresa en el id de grupo del archivo de Datos
			 * (lo obtengo a partir de cualquiera de los registros del bucket) los datos,
			 * a menos que sea griego RF
			 * ---Si el resultado da OVERFLOW, se duplica en memoria la tabla de hash,
			 * y se reasignan los registros.
			 * -Ingreso los valores en el archivo de datos
			 * --Si es Griego, RF: Se crea un grupo y all� se coloca el registro
			 * --Si es Griego, RLV: Se inserta en el grupo donde estan los otros registros
			 * --Si es Romano: Se inserta en el grupo.
			 * --En los ultimos 2 casos, puede dar OVERFLOW. Se trata del mismo modo que
			 * un OVERFLOW en el archivo de Indices
			 */
			unsigned char* listaBusqueda = (unsigned char*)malloc(1);
			int ind = 0;
			for (list< string>::iterator iLista = clave->ValoresBusqueda.begin(); iLista!=  clave->ValoresBusqueda.end(); iLista++){
				string valorBusq = *iLista;
				//lo paso a un char*
				char* charBusq = new char[valorBusq.length() + 1];
				charBusq = strcpy(charBusq, valorBusq.c_str());
				listaBusqueda = (unsigned char*)realloc(listaBusqueda, strlen(charBusq));
				listaBusqueda = (unsigned char*)memcpy((void*)&listaBusqueda[ind], (void*)charBusq, strlen(charBusq));
				ind += strlen(charBusq);
			}
			listaBusqueda[ind] = '\n';
			long resHash = funcionHash(listaBusqueda, ind, 2^directoryDepth);
			long offsetBucket = tablaHash[resHash];
			CodigoResultado resInsInd = Exito;
			CodigoResultado resInsDat = Exito;
			Grupo *grupoOverf = new Grupo();
			if (offsetBucket == -1){
				//creo el grupo
				Grupo *nuevoGrupo = new Grupo();
				Grupo *nuevoGrupoDatos = new Grupo();
				tablaHash[resHash] = this->ArchivoIndice->CrearGrupo(*nuevoGrupo);
				int idGrupoDatos = this->ArchivoDatos->CrearGrupo(*nuevoGrupoDatos);
				nuevoGrupo->Atributos.insert(nuevoGrupo->Atributos.end(), Atributo("Cantidad", "1"));
				Registro *nuevoRegistro = new Registro();
				for (list<Condicion*>::iterator iAtrib = clave->OtrasCondiciones->Subcondiciones.begin(); iAtrib != clave->OtrasCondiciones->Subcondiciones.end(); iAtrib++){
					nuevoRegistro->Atributos.insert(nuevoRegistro->Atributos.end(), ((Condicion*)*iAtrib)->ValorCondicion);
				}
				nuevoRegistro->Atributos.insert(nuevoRegistro->Atributos.end(), Atributo("offsetGrupo", IntToString(idGrupoDatos)));
				nuevoGrupo->Registros.insert(nuevoGrupo->Registros.begin(), *nuevoRegistro);
				//creo el grupo de datos
				Registro *nuevoRegistroDatos = new Registro();
				for (list<Condicion*>::iterator iAtrib = clave->OtrasCondiciones->Subcondiciones.begin(); iAtrib != clave->OtrasCondiciones->Subcondiciones.end(); iAtrib++){
					nuevoRegistroDatos->Atributos.insert(nuevoRegistroDatos->Atributos.end(), ((Condicion*)*iAtrib)->ValorCondicion);
				}
				nuevoRegistroDatos->Atributos.insert(nuevoRegistroDatos->Atributos.end(), clave->DatosAInsertar.begin(), clave->DatosAInsertar.end());
				nuevoGrupoDatos->Registros.insert(nuevoGrupoDatos->Registros.begin(), *nuevoRegistroDatos);
				this->ArchivoIndice->GuardarGrupo(*nuevoGrupo);
				this->ArchivoDatos->GuardarGrupo(*nuevoGrupoDatos);
			}else{//tengo un id de bucket
				if (organizacionReg == "RF" && tipoIndice == Griego){
					//se agrega en uno separado
					Grupo nuevoGrupo = this->ArchivoIndice->LeerGrupo(offsetBucket);
					Grupo nuevoGrupoDatos = Grupo();
					int idGrupoDatos = this->ArchivoDatos->CrearGrupo(nuevoGrupoDatos);
					Registro *nuevoRegistro = new Registro();
					for (list<Condicion*>::iterator iAtrib = clave->OtrasCondiciones->Subcondiciones.begin(); iAtrib != clave->OtrasCondiciones->Subcondiciones.end(); iAtrib++){
						nuevoRegistro->Atributos.insert(nuevoRegistro->Atributos.end(), ((Condicion*)*iAtrib)->ValorCondicion);
					}
					nuevoRegistro->Atributos.insert(nuevoRegistro->Atributos.end(), Atributo("offsetGrupo", IntToString(idGrupoDatos)));
					nuevoGrupo.Registros.insert(nuevoGrupo.Registros.end(), *nuevoRegistro);
					//creo el grupo de datos
					Registro *nuevoRegistroDatos = new Registro();
					for (list<Condicion*>::iterator iAtrib = clave->OtrasCondiciones->Subcondiciones.begin(); iAtrib != clave->OtrasCondiciones->Subcondiciones.end(); iAtrib++){
						nuevoRegistroDatos->Atributos.insert(nuevoRegistroDatos->Atributos.end(), ((Condicion*)*iAtrib)->ValorCondicion);
					}
					nuevoRegistroDatos->Atributos.insert(nuevoRegistroDatos->Atributos.end(), clave->DatosAInsertar.begin(), clave->DatosAInsertar.end());
					nuevoGrupoDatos.Registros.insert(nuevoGrupoDatos.Registros.begin(), *nuevoRegistroDatos);
					this->ArchivoIndice->GuardarGrupo(nuevoGrupo);
					this->ArchivoDatos->GuardarGrupo(nuevoGrupoDatos);
				}
				else{//se graba en el mismo bucket
					Grupo grupoIndice = this->ArchivoIndice->LeerGrupo(offsetBucket);
					Registro regIndice = *grupoIndice.Registros.begin();
					string offsetDatos = "";
					for(list<Atributo>::iterator iAtrib = regIndice.Atributos.begin(); iAtrib != regIndice.Atributos.end(); iAtrib++){
						Atributo atrib = *iAtrib;
						if (atrib.Nombre == "offsetGrupo")
							offsetDatos = atrib.Valor;
					}
					if (offsetDatos == "")
						return; //aunque esto no debera ocurrir nunca
					int offsetDatosAsInt;
					char* charBusq = new char[offsetDatos.length() + 1];
					charBusq = strcpy(charBusq, offsetDatos.c_str());
					offsetDatosAsInt = atoi(charBusq);

					Grupo grupoDatos = this->ArchivoDatos->LeerGrupo(offsetDatosAsInt);
					//lo ingreso en el registro de datos y en el de indice
					for (list<Condicion*>::iterator iAtrib = clave->OtrasCondiciones->Subcondiciones.begin(); iAtrib != clave->OtrasCondiciones->Subcondiciones.end(); iAtrib++){
						regIndice.Atributos.insert(regIndice.Atributos.end(), ((Condicion*)*iAtrib)->ValorCondicion);
					}
					regIndice.Atributos.insert(regIndice.Atributos.end(), Atributo("offsetGrupo", offsetDatos));
					grupoIndice.Registros.insert(grupoIndice.Registros.end(), regIndice);
					//creo el grupo de datos
					Registro *nuevoRegistroDatos = new Registro();
					for (list<Condicion*>::iterator iAtrib = clave->OtrasCondiciones->Subcondiciones.begin(); iAtrib != clave->OtrasCondiciones->Subcondiciones.end(); iAtrib++){
						nuevoRegistroDatos->Atributos.insert(nuevoRegistroDatos->Atributos.end(), ((Condicion*)*iAtrib)->ValorCondicion);
					}
					nuevoRegistroDatos->Atributos.insert(nuevoRegistroDatos->Atributos.end(), clave->DatosAInsertar.begin(), clave->DatosAInsertar.end());
					grupoDatos.Registros.insert(grupoDatos.Registros.begin(), *nuevoRegistroDatos);
					resInsInd = this->ArchivoIndice->GuardarGrupo(grupoIndice);
					resInsDat = this->ArchivoDatos->GuardarGrupo(grupoDatos);
					if (resInsInd == Sobreflujo){
						this->ArchivoIndice->CrearGrupo(*grupoOverf);
						grupoOverf = &grupoIndice;
					}
					if (resInsDat == Sobreflujo){
						this->ArchivoDatos->CrearGrupo(*grupoOverf);
						grupoOverf = &grupoDatos;
					}
				}
			}
			if (resInsInd == Sobreflujo || resInsDat == Sobreflujo){
				/*
				 * Hay que dividir la tabla y reposicionar los registros.
				 * Para eso, los paso primero a un archivo temporal
				 */
				this->directoryDepth++;
				delete(tablaHash);
				long cantidadCampos = (long)2^directoryDepth;
				tablaHash = new long[cantidadCampos]();
				for(int k = 0; k < cantidadCampos; k++)
					tablaHash[k] = -1; //inicializo con valores -1
				/*
				 * creo un archivo secuencial y lo cargo con los grupos del
				 * archivo de datos secuencialmente,
				 * incluido el ultimo grupo que estaba queriendo ingresar
				 */
				ArchivoRLV *archAux = new ArchivoRLV(this->ArchivoDatos->Nombre + "secTemp");
				MetadatosArchivo metArch = MetadatosArchivo(this->ArchivoDatos->Metadatos->Nombre + "secDataTemp");
				MetadatosGrupo *metGrupo = new MetadatosGrupo();
				metGrupo->MetadatosAtributos = this->ArchivoDatos->Metadatos->MetadatosGrupo->MetadatosAtributos;
				metGrupo->MetadatosRegistro = new MetadatosRegistro();
				metGrupo->MetadatosRegistro->MetadatosAtributos = this->ArchivoDatos->Metadatos->MetadatosGrupo->MetadatosRegistro->MetadatosAtributos;
				metArch.MetadatosGrupo = metGrupo;
				archAux->Metadatos->MetadatosGrupo = metGrupo;
				archAux->Metadatos = &metArch;
				//lo lleno
				int grupoId = this->ArchivoDatos->PrimerGrupo();
				int grupoAnterior;
				while (grupoId != -1){
					grupoAnterior = grupoId;
					Grupo grupoActual = this->ArchivoDatos->LeerGrupo(grupoId);
					archAux->GuardarGrupo(grupoActual);
					//borro el grupo. Con una llamada a borrar de cada clave para borrar el archivo de indice tambien
					int cant;
					Clave* claveDatos = ObtenerClavesPorGrupo(grupoActual, true, &cant);
					for (int j = 0; j < cant; j++){
						EliminarClave(&claveDatos[j]);
					}
					grupoId = this->ArchivoDatos->SiguienteGrupo(grupoId);
				}
				//borro el ultimo, asi lo ingreso con los nuevos datos
				archAux->BorrarGrupo(grupoAnterior);
				archAux->GuardarGrupo(*grupoOverf);
				//Estan los Archivos de Indice y Datos limpios. Le inserto todos los grupos que ya estaban
				grupoId = archAux->PrimerGrupo();
				while (grupoId != -1){
					Grupo grupoActual = archAux->LeerGrupo(grupoId);
					//a partir del grupo armo la clave y llamo a insertar
					int cant;
					Clave* claveAInsertar = ObtenerClavesPorGrupo(grupoActual, false, &cant);
					for (int j = 0; j < cant; j++){
						InsertarClave(&claveAInsertar[j]);
					}
					//Paso al siguiente
					grupoId = this->ArchivoDatos->SiguienteGrupo(grupoId);
				}
			}


		}

		void HashExtensible::BuscarClave(Clave* clave){

			///*La primer condicion es que los Atributos que estan en clave->CondicionesLocales
			//   se correspondan con todos los campos de la clave por la cual esta desarrollada el hash*/
			//map	<string, int>* mapaClaves = new map<string, int>();
			////inserto todas las claves por el nombre con un 0 (implica que no esta siendo usada en las CondicionesLocales
			//for (IteradorDeMetadatosAtributos i = Metadata->Claves.begin(); i!= Metadata->Claves.end();i++){
			//	MetadatosAtributo atrib = *i;
			//	mapaClaves->insert(pair<string, int>(atrib.Nombre, 0));
			//}
			////recorro las condiciones locales y voy poniendo 1's a aquellas que est�n
			//for (map< string, list<Condicion*> >::iterator j = clave->CondicionesLocales->begin(); j!=  clave->CondicionesLocales->end();j++){
			//	pair< string, list<Condicion*> > atrib = *j;
			//	map	<string, int>::iterator iterEncont = mapaClaves->find(atrib.first);
			//	if (iterEncont == NULL){
			//		//Es un error. Deberia estar la condicion de busqueda
			//		throw exception();
			//	}
			//	pair<string, int> mapaEncont = *iterEncont;
			//	if (mapaEncont.second == 1){
			//		//Es un error. Esta repetido
			//		throw exception();
			//	}
			//	mapaEncont.first = 1;
			//}


			/*
			 * Aqui ya est� validado que las condiciones de busqueda sean las correctas.
			 * Lo que hay que hacer ahora es:
			 * -Con las condiciones locales y los valores de busqueda convertirlos por hash
			 * y obtener el valor representativo
			 * -Buscarlo en el bucket correspondiente recorriendo todos los registros seg�n los
			 * valores de busqueda.
			 * -Si est�n esos valores de b�squeda, se sigue la busqueda en el archivo de datos,
			 * seg�n el tipo de �ndice se devuelve el registro dentro del grupo que cumple con esa clave,
			 * o una lista de todos los registros con sus claves primarias correspondientes.
			 */
			//Convierto la lista de string con los valores de busqueda a un unico string y le aplico la funcion de hash
			unsigned char* listaBusqueda;
			int ind = 0;
			for (list< string>::iterator iLista = clave->ValoresBusqueda.begin(); iLista!=  clave->ValoresBusqueda.end(); iLista++){
				string valorBusq = *iLista;
				//lo paso a un char*
				char* charBusq = new char[valorBusq.length() + 1];
				charBusq = strcpy(charBusq, valorBusq.c_str());
				listaBusqueda = (unsigned char*)realloc(listaBusqueda, strlen(charBusq));
				listaBusqueda = (unsigned char*)memcpy((void*)listaBusqueda[ind], (void*)charBusq, strlen(charBusq));
				ind += strlen(charBusq);
			}
			listaBusqueda[ind] = '\n';
			long resHash = funcionHash(listaBusqueda, ind, 2^directoryDepth);
			long offsetBucket = tablaHash[resHash];
			if (offsetBucket == -1)
				return;
			//leo el id de grupo del archivo de buckets
			Grupo grupoIndice = this->ArchivoIndice->LeerGrupo(offsetBucket);
			//en el bucket tengo varios registros. Tengo que recorrerlos hasta encontrar el que coincide con todos los criterios de busqueda
			//Si es que lo encuentro puedo acceder al id del Grupo de Datos
			int idGrupoDatos = -1;
			for (IteradorDeRegistros iterRegGrupo =	grupoIndice.Registros.begin(); iterRegGrupo != grupoIndice.Registros.end(); iterRegGrupo++){
				Registro registroIndice = *iterRegGrupo;
				if (clave->OtrasCondiciones->Analizar(registroIndice)){
					//cumple las condiciones. Obtengo el id del grupo de datos
					for (IteradorDeClaveValor i = Metadata->Atributos.begin(); i!= Metadata->Atributos.end();i++)
					{
						ClaveValor cv = *i;
						if (cv.Clave == "offsetGrupo")
							idGrupoDatos = (int) atoi(&cv.Valor[0]);
					}
				}
			}
			if (idGrupoDatos == -1){
				return;
			}
			Grupo grupoDatos = this->ArchivoDatos->LeerGrupo(idGrupoDatos);
			list<Registro> *regDatos = new list<Registro>();
			switch (tipoIndice){
				case Griego:
					if (organizacionReg == "RF"){
						//Est� directamente el registro. Lo unico que verifico es que sea ese el que busco, y no otro (el que busco no esta)
						Registro regAux = *grupoDatos.Registros.begin();
						if (clave->OtrasCondiciones->Analizar(regAux))
							regDatos->insert(regDatos->begin(), regAux);

					}else{//RLV
						//Recorro los registros hasta encontrar el que cumple con la clave de busqueda. Voy a encontrar uno solo dado que es un indice Griego
						for (list<Registro>::iterator iRegDatos = grupoDatos.Registros.begin(); iRegDatos != grupoDatos.Registros.end(); iRegDatos++){
							if (clave->OtrasCondiciones->Analizar(*iRegDatos)){
								regDatos->insert(regDatos->begin(), *iRegDatos);
								break;//tengo que encontrar solo uno
							}
						}
					}
					break;
				case Romano:
					//Tengo que recorrer todos los registros y devolver los datos de todos aquellos que cumplan
					for (list<Registro>::iterator iRegDatos = grupoDatos.Registros.begin(); iRegDatos != grupoDatos.Registros.end(); iRegDatos++){
						if (clave->OtrasCondiciones->Analizar(*iRegDatos)){
							regDatos->insert(regDatos->begin(), *iRegDatos);
						}
					}
					break;

			}
			for (list<Registro>::iterator iResultado = regDatos->begin(); iResultado!= regDatos->end(); iResultado++){
				clave->Resultado->insert(clave->Resultado->end(), *iResultado);
			}

		}

		void HashExtensible::EliminarClave(Clave* clave){
			//lo busco segun la tabla de Hash
			clave->RegistrosAfectados = 0;
			unsigned char* listaBusqueda;
			int ind = 0;
			for (list< string>::iterator iLista = clave->ValoresBusqueda.begin(); iLista!=  clave->ValoresBusqueda.end(); iLista++){
				string valorBusq = *iLista;
				//lo paso a un char*
				char* charBusq = new char[valorBusq.length() + 1];
				charBusq = strcpy(charBusq, valorBusq.c_str());
				listaBusqueda = (unsigned char*)realloc(listaBusqueda, strlen(charBusq));
				listaBusqueda = (unsigned char*)memcpy((void*)listaBusqueda[ind], (void*)charBusq, strlen(charBusq));
				ind += strlen(charBusq);
			}
			listaBusqueda[ind] = '\n';
			long resHash = funcionHash(listaBusqueda, ind, 2^directoryDepth);
			long offsetBucket = tablaHash[resHash];
			//me posiciono en el grupo de Indice y busco cual es el/los registro/s que cumplen
			if (offsetBucket == -1){
				return;
			}
			//leo el grupo
			Grupo grupoIndice = this->ArchivoIndice->LeerGrupo(offsetBucket);
			list<int> offsetDatosABorrar = list<int>(); //son los grupos del archivo de datos donde hay registros a borrar
			for (list<Registro>::iterator iReg = grupoIndice.Registros.begin(); iReg != grupoIndice.Registros.end(); iReg++){
				Registro regIndice = *iReg;
				if (clave->OtrasCondiciones->Analizar(regIndice)){
					/*
					 * Es un candidato a borrar.
					 * Guardo el offset en un array, y borro el registro.
					 */
					string offsetDatos = "";
					for(list<Atributo>::iterator iAtrib = regIndice.Atributos.begin(); iAtrib != regIndice.Atributos.end(); iAtrib++){
						Atributo atrib = *iAtrib;
						if (atrib.Nombre == "offsetGrupo")
							offsetDatos = atrib.Valor;
					}
					if (offsetDatos != ""){
						int offsetDatosAsInt;
						char* charBusq = new char[offsetDatos.length() + 1];
						charBusq = strcpy(charBusq, offsetDatos.c_str());
						offsetDatosAsInt = atoi(charBusq);
						offsetDatosABorrar.insert(offsetDatosABorrar.end(), offsetDatosAsInt);
						//borro el registro
						grupoIndice.Registros.erase(iReg);
						clave->RegistrosAfectados++;
					}
				}
			}
			this->ArchivoIndice->GuardarGrupo(grupoIndice);
			//para cada grupo a borrar, lo leo, verifico que registros cumplen las condiciones y los borro
			for (list<int>::iterator iGrupo = offsetDatosABorrar.begin(); iGrupo != offsetDatosABorrar.end(); iGrupo++){
				int offsetALeer = *iGrupo;
				Grupo grupoDatos = this->ArchivoDatos->LeerGrupo(offsetALeer);
				for (list<Registro>::iterator iReg = grupoDatos.Registros.begin(); iReg != grupoDatos.Registros.end(); iReg++){
					Registro regDatos;
					if (clave->OtrasCondiciones->Analizar(regDatos)){
						//borro el registro
						grupoDatos.Registros.erase(iReg);
					}
				}
				this->ArchivoDatos->GuardarGrupo(grupoDatos);
			}
			/*
			 * Me fijo si es necesario achicar la tabla de hash.
			 * Hay que ver si un grupo formado por el campo de Hash 'i'
			 * y su correspondiente 'i' + TOTAL/2; con i variando de 0 a TOTAL/2
			 */
			//creo un archivo auxiliar de buckets
			ArchivoRLV* ArchivoIndiceAux;
			MetadatosArchivo metArchIndice = MetadatosArchivo(Metadata->NombreArchivo + "AuxIndice");
			MetadatosGrupo* metGrupo = new MetadatosGrupo();
			MetadatosRegistro* metRegistro = new MetadatosRegistro(); //SUPONEMOS QUE EL CONSTRUCTOR CREA TAMBIEN LAS LISTAS CONTENIDAS
			// Agrega los atributos que tiene el grupo:
			MetadatosAtributo* cantidad = new MetadatosAtributo("Cantidad",Entero);
			metGrupo->MetadatosAtributos.insert(metGrupo->MetadatosAtributos.begin(),*cantidad);
			MetadatosAtributo* prof = new MetadatosAtributo("Profundidad",Entero);
			metGrupo->MetadatosAtributos.insert(metGrupo->MetadatosAtributos.begin(),*prof);
			// Seteo los atributos que van a tener los registros, incluidos en cada grupo
			// Agrega los campos de la clave
			metRegistro->MetadatosAtributos = Metadata->Claves;
			metRegistro->MetadatosAtributos.insert(metRegistro->MetadatosAtributos.end(), MetadatosAtributo("offsetGrupo", EnteroLargo));
			metGrupo->MetadatosRegistro = metRegistro;
			metArchIndice.MetadatosGrupo = metGrupo;
			ArchivoIndiceAux = new ArchivoRLV(metArchIndice);//ArchivoRLV
			//ArchivoIndiceAux->Metadatos = &metArchIndice;
			//
			int Total = 2^directoryDepth;
			bool sePuede = true;
			for (int iHash = 0; iHash < Total / 2; iHash++){
				int id1 = tablaHash[iHash];
				int id2 = tablaHash[iHash + Total /2];
				Grupo g1 = this->ArchivoIndice->LeerGrupo(id1);
				Grupo g2 = this->ArchivoIndice->LeerGrupo(id2);
				Grupo *grupoPrueba = new Grupo();
				for (list<Registro>::iterator iReg = g1.Registros.begin(); iReg != g1.Registros.end(); iReg++){
					Registro reg = *iReg;
					grupoPrueba->Registros.insert(grupoPrueba->Registros.end(), reg);
				}
				for (list<Registro>::iterator iReg = g2.Registros.begin(); iReg != g2.Registros.end(); iReg++){
					Registro reg = *iReg;
					grupoPrueba->Registros.insert(grupoPrueba->Registros.end(), reg);
				}
				CodigoResultado res = ArchivoIndiceAux->GuardarGrupo(*grupoPrueba);
				if (res == Sobreflujo){
					sePuede = false;
					break;
				}
			}
			if (sePuede){
				//achicamos la tabla de hash
				directoryDepth--;
				for (int iHash = 0; iHash < Total / 2; iHash++){
					int id1 = tablaHash[iHash];
					int id2 = tablaHash[iHash + Total /2];
					Grupo g1 = this->ArchivoIndice->LeerGrupo(id1);
					Grupo g2 = this->ArchivoIndice->LeerGrupo(id2);
					for (list<Registro>::iterator iReg = g2.Registros.begin(); iReg != g2.Registros.end(); iReg++){
						Registro reg = *iReg;
						g1.Registros.insert(g1.Registros.end(), reg);
					}
					this->ArchivoIndice->GuardarGrupo(g1);
					this->ArchivoIndice->BorrarGrupo(id2);
					tablaHash[iHash + Total /2] = -1;
				}

			}

		}

		void HashExtensible::ActualizarClave(Clave* clave){
			//lo busco segun la tabla de Hash
			clave->RegistrosAfectados = 0;
			unsigned char* listaBusqueda;
			int ind = 0;
			for (list< string>::iterator iLista = clave->ValoresBusqueda.begin(); iLista!=  clave->ValoresBusqueda.end(); iLista++){
				string valorBusq = *iLista;
				//lo paso a un char*
				char* charBusq = new char[valorBusq.length() + 1];
				charBusq = strcpy(charBusq, valorBusq.c_str());
				listaBusqueda = (unsigned char*)realloc(listaBusqueda, strlen(charBusq));
				listaBusqueda = (unsigned char*)memcpy((void*)listaBusqueda[ind], (void*)charBusq, strlen(charBusq));
				ind += strlen(charBusq);
			}
			listaBusqueda[ind] = '\n';
			long resHash = funcionHash(listaBusqueda, ind, 2^directoryDepth);
			long offsetBucket = tablaHash[resHash];
			//me posiciono en el grupo de Indice y busco cual es el/los registro/s que cumplen
			if (offsetBucket == -1){
				return;
			}
			//leo el grupo
			Grupo grupoIndice = this->ArchivoIndice->LeerGrupo(offsetBucket);
			for (list<Registro>::iterator iReg = grupoIndice.Registros.begin(); iReg != grupoIndice.Registros.end(); iReg++){
				Registro regIndice = *iReg;
				if (clave->OtrasCondiciones->Analizar(regIndice)){
					//Busco en el grupo al que apunta este registo, que registros de datos se deben actualizar
					string offsetDatos = "";
					for(list<Atributo>::iterator iAtrib = regIndice.Atributos.begin(); iAtrib != regIndice.Atributos.end(); iAtrib++){
						Atributo atrib = *iAtrib;
						if (atrib.Nombre == "offsetGrupo")
							offsetDatos = atrib.Valor;
					}
					if (offsetDatos != ""){
						int offsetALeer;
						char* charBusq = new char[offsetDatos.length() + 1];
						charBusq = strcpy(charBusq, offsetDatos.c_str());
						offsetALeer = atoi(charBusq);
						Grupo grupoDatos = this->ArchivoDatos->LeerGrupo(offsetALeer);
						for (list<Registro>::iterator iReg = grupoDatos.Registros.begin(); iReg != grupoDatos.Registros.end(); iReg++){
							Registro regDatos;
							if (clave->OtrasCondiciones->Analizar(regDatos)){
								//actualizo el registro. Recorro los atributos, y si hay alguno en la listaAInsertar, modifico su valor
								for (list<Atributo>::iterator iAtrib = regDatos.Atributos.begin(); iAtrib != regDatos.Atributos.end(); iAtrib++){
									Atributo atrib = *iAtrib;
									for (list<Atributo>::iterator iAtribAIns = clave->DatosAInsertar.begin(); iAtribAIns != clave->DatosAInsertar.end(); iAtribAIns++){
										Atributo atribAIns = *iAtribAIns;
										if (atribAIns.Nombre == atrib.Nombre){
											//hay que actualizar este atributo
											atrib.Valor = atribAIns.Valor;
										}
									}
								}
								grupoDatos.Registros.erase(iReg);
							}
						}
						this->ArchivoDatos->GuardarGrupo(grupoDatos);
					}
				}
			}
		}

		long HashExtensible::funcionHash(unsigned char* input, int cantBytes, long mod){
			long  suma = 0;
			for (int i = 0; i < cantBytes; i++){
				char c = input[i];
				suma += c;
			}
			return (suma % mod);
		}

		Clave* HashExtensible::ObtenerClavesPorGrupo(Grupo grupo, bool soloClaves, int *cantClaves){
			Clave* clavesRetorno = new Clave[grupo.Registros.size()]();
			clavesRetorno->OtrasCondiciones = new CondicionCompuesta();
			int contadorClaves = 0;
			for (list<Registro>::iterator iReg = grupo.Registros.begin(); iReg != grupo.Registros.end(); iReg++){
				Registro reg = *iReg;
				for (list<Atributo>::iterator iAtrib = reg.Atributos.begin(); iAtrib != reg.Atributos.end(); iAtrib++){
					Atributo atrib = *iAtrib;
					string nombre = atrib.Nombre;
					bool esClave = false;
					for (list<MetadatosAtributo>::iterator iMetAtrib = Metadata->Claves.begin(); iMetAtrib != Metadata->Claves.end(); iMetAtrib++){
						MetadatosAtributo metAtrib = *iMetAtrib;
						if (metAtrib.Nombre == nombre){
							esClave = true;
							break;
						}
					}
					if (esClave){
						CondicionCompuesta cond = CondicionCompuesta(); //creo una compuesta porque condicion es abstracta
						cond.ValorCondicion = atrib;
						cond.Simbolo = Igual;
						clavesRetorno[contadorClaves].OtrasCondiciones->Subcondiciones.insert(clavesRetorno[contadorClaves].OtrasCondiciones->Subcondiciones.end(), &cond);
					}else if (!soloClaves){
						clavesRetorno[contadorClaves].DatosAInsertar.insert(clavesRetorno[contadorClaves].DatosAInsertar.end(), atrib);
					}

				}
				contadorClaves++;
			}
			cantClaves = &contadorClaves;
			return clavesRetorno;
		}

		HashExtensible::~HashExtensible(){
			//paso la tabla de hash al disco nuevamente
			int cantidadCampos = 2^directoryDepth;
		    Grupo *grupoHash = new Grupo();
		    //borro todos los grupos que tiene
		    int numGrupo = this->ArchivoHash->PrimerGrupo();
		    while (numGrupo != -1){
		    	this->ArchivoHash->BorrarGrupo(numGrupo);
		    	numGrupo = this->ArchivoHash->SiguienteGrupo(numGrupo);
		    }
		    //grabo el nuevo archivo
		    this->ArchivoHash->CrearGrupo(*grupoHash);
		    char* cadenaHash = (char*)malloc(ARCH_HASH_LONG_CADENA);
			int i;
			for (i = 0; i < cantidadCampos; i++){
				long valor = tablaHash[i];
				string valString = IntToString(valor);
				for (int k = 0; k < valString.length(); k++){
					cadenaHash[i * ARCH_HASH_LONG_CADENA + k] = valString[k];
				}
				cadenaHash = (char*)realloc(cadenaHash, (i+1) * ARCH_HASH_LONG_CADENA);
			}
			cadenaHash = (char*)realloc(cadenaHash, (i+1) * ARCH_HASH_LONG_CADENA + 1);
			cadenaHash[(i+1) * ARCH_HASH_LONG_CADENA + 1] = '\n';
			Atributo *atribHash = new Atributo();
			atribHash->Nombre = "OffsetBucket";
			atribHash->Tipo = Cadena;
			//paso a un string
			string valor = string();
			for (unsigned int j = 0; j < strlen(cadenaHash); j++){
				valor += cadenaHash[j];
			}
			atribHash->Valor = valor;
			grupoHash->Atributos.insert(grupoHash->Atributos.end(), *atribHash);
			this->ArchivoHash->GuardarGrupo(*grupoHash);
			//habra que ver aqu si existe la posibilidad de desborde.

		}

		CapaIndices::IIndice::~IIndice(){}

/*
		ArchivoTemp::ArchivoTemp(string nombre):IArchivo(){
			Nombre = nombre;
			grupos = new list<Grupo>();
			Metadatos = new MetadatosArchivo(nombre);
		}

		ArchivoTemp::ArchivoTemp(string nombre, MetadatosArchivo& metadatosDelArchivo):IArchivo(){
			Nombre = nombre;
			grupos = new list<Grupo>();
			Metadatos = &metadatosDelArchivo;
		}*/

}
